#! /usr/bin/env python2.6
'''
a utility to perform sql-like process on table in text files format
'''
import sys
import re

# kinds of functions:
# map like functions
# [a] -> [b] : Select filter Expression
# reduce like functions
# [a] -> b : count sum min max
# reorder functions
# [a] -> [a]
# complicated functions
# [a] [b] -> [c] groupby join

def serialize(obj, stream, seperator="\t,"):
	''' serialize table object to stream'''
	for line in obj:
		eles = []
		for obj in line:
			rep = None
			if isinstance(obj, list):
				rep = seperator[1].join([str(e) for e in obj])
			else:
				rep = str(obj)
			eles.append(rep)
		stream.write(seperator[0].join(eles))
		stream.write("\n")

def deserialize(stream, seperator="\t,"):
	''' deserialize stream to table object'''
	# second level separator pattern
	multi_field_pattern = re.compile('[0-9,]*,[0-9,]*')
	ret = []
	for line in stream:
		if line[-1] == '\n':
			line = line[:-1]
		if len(line) == 0:
			continue
		strs = line.split(seperator[0])
		temp = []
		for str_ele in strs:
			if multi_field_pattern.match(str_ele):
				temp.append(str_ele.split(','))
			else:
				temp.append(str_ele)
		ret.append(temp)
	return ret

class Processor(object):
	''' base processor class
	    Processor.process transform a table object to anothor'''
	def __init__(self, desc_str):
		self.desc = desc_str
	def process(self, obj):
		''' process interface'''
		pass
	@staticmethod
	def create(desc_str):
		''' create a processor from parsing a string'''
		if len(desc_str) == 0:
			return Equal(desc_str)
		type_char = desc_str[0].lower()
		ret_processor = None
		if type_char == 's':
			ret_processor = Select(desc_str[1:])
		elif type_char == 'f':
			ret_processor = Pfilter(desc_str[1:])
		elif type_char == 'e':
			ret_processor = Expression(desc_str[1:])
		elif type_char == 'u':
			ret_processor = SumAggregator(desc_str[1:])
		elif type_char == '[' and desc_str[-1] == ']':
			ret_processor = Compositor(desc_str[1:-1])
		return ret_processor

class Equal(Processor):
	''' do nothing processor'''
	def __init__(self, desc_str):
		Processor.__init__(self, desc_str)
	def process(self, obj):
		''' interface impl'''
		return obj

class Select(Processor):
	''' select some fields from original record
	    e.g. s013 select row 0,1,3 from original record'''
	def __init__(self, desc_str):
		Processor.__init__(self, desc_str)
		self.positions = [ord(c)-ord('0') for c in desc_str]
	def process(self, obj):
		''' process interface impl'''
		if not isinstance(obj, list):
			obj = [obj]
		return [obj[index] for index in self.positions]

class Pfilter(Processor):
	''' filter record based on a python expression
	    element is named as 'x'
	    keep this line if function return True, else return None
	    e.g. int(x[0])%2==0
	'''
	def __init__(self, desc_str):
		Processor.__init__(self, desc_str)
	def process(self, obj):
		''' process interface impl'''
		if eval(self.desc, None, {"x":obj}):
			return obj
		else:
			return None

class Expression(Processor):
	''' transform a record to another using a python expression
	    e.g. [int(x[0])+int(x[1]), int(x[2])+int(x[3])]
	'''
	def __init__(self, desc_str):
		Processor.__init__(self, desc_str)
	def process(self, obj):
		''' using eval for simplicity'''
		return eval(self.desc, None, {"x":obj})

class SumAggregator(Processor):
	''' perform sum on a table '''
	def __init__(self, desc_str):
		Processor.__init__(self, desc_str)
		if len(desc_str) == 0 or desc_str[0] == 'i':
			self.parse_func = int
		else:
			self.parse_func = float
	def process(self, obj):
		''' add all '''
		if isinstance(obj, list):
			if len(obj) == 0:
				return None
			totals = [0 for ele in obj[0]]
			for row in obj:
				for i in xrange(len(row)):
					totals[i] = totals[i] + self.parse_func(row[i])
			return totals
		else:
			return obj

class SingleRowProcessor(Processor):
	''' Wrap a Processor to use only one row in a input'''
	def __init__(self, row_no, processor):
		Processor.__init__(self, "%d%s" % (row_no, processor.desc))
		self.row_no = row_no
		self.processor = processor
	def process(self, obj):
		''' processor wrapper'''
		return self.processor.process(obj[self.row_no])

class Compositor(Processor):
	''' make compositions of other processors'''
	@staticmethod
	def parse_children_desc(desc):
		''' parse string to processor'''
		if len(desc) == 0:
			return None
		if desc[0] >= '0' and desc[0] <= '9':
			if len(desc) >=2 and desc[1] >= '0' and desc[1] <= '9':
				return SingleRowProcessor(int(desc[:2]), Processor.create(desc[2:]))
			else:
				return SingleRowProcessor(int(desc[0]), Processor.create(desc[1:]))
		else:
			return Processor.create(desc)
	def __init__(self, desc_str):
		Processor.__init__(self, desc_str)
		self.children_desc = desc_str.split('::')
		self.children = [Compositor.parse_children_desc(ele_desc)
						 for ele_desc in self.children_desc]
	def process(self, obj):
		''' process impl'''
		ret = []
		for proc in self.children:
			if proc:
				ret.append(proc.process(obj))
			else:
				ret.append(None)
		return ret

class Mapper(Processor):
	''' table level transform using Select, Pfilter or Expression'''
	def __init__(self, desc_str):
		Processor.__init__(self, desc_str)
		self.ele_processor = Processor.create(desc_str)
	def process(self, obj):
		''' map table using underlying record processor'''
		if isinstance(obj, list):
			ret = [self.ele_processor.process(ele) for ele in obj]
			return [ele for ele in ret if ele]
		else:
			return self.ele_processor.process(obj)

# main function
if __name__ == "__main__":
	fin = sys.stdin
	fout = sys.stdout
	processor_expression = sys.argv[1]
	matrix_in = deserialize(fin)
	mprocesser = Mapper(processor_expression)
	matrix_out = mprocesser.process(matrix_in)
	serialize(matrix_out, fout)
